#include <windows.h>
#include "meshManager.h"
#include <gl/GL.h>

//---------------------------------------------------- LoadModel -------------------------------------------------
bool MeshManager::LoadModel( const char* path, const char* name )
{
	string spath( path );
	string sname( name );
	DataInModel dataInModel;

	if( namesMap.count( sname ) == 1 )
	{
		lastError = "MeshManager::LoadModel failed. Name " + spath + " already in names map!\n";
		return false;
	}

	//tworzymy nowy model Obj
	pObjModel = new ObjModel();

	//ladujemy dane do modelu obj
	if( ! objMeshLoader.LoadObjModel( path, *pObjModel ) )
	{
		delete pObjModel;
		lastError = "MeshManager::LoadModel failed. LoadObjModel failed to load " + sname + " model!\n";
		return false;
	}
	dataInModel = pObjModel->GetDataType();

	//tworzymy model VBO 
	int polygonType;
	if( pObjModel->GetNumberOfVerticesPerFace() == 3 )
		polygonType = GL_TRIANGLES;
	else
		polygonType = GL_QUADS;

	VBOModel* pVBOModel = new VBOModel( path, name, polygonType, pObjModel->modelElements.size(), dataInModel );

	//konwertujemy dane z ObjModel -> VBOModel
	if( dataInModel == V_AND_VT_AND_VN_DATA )
	{
		if( !RearangeData_v_vt_vn( *pObjModel, *pVBOModel ) )
		{
			delete pObjModel;
			delete pVBOModel;
			lastError = "MeshManager::LoadModel failed. RearangeData_v_vt_vn failed to convert " + sname + " model!\n";
			return false;
		}
	}
	else
	if( dataInModel == V_AND_VT_DATA )
	{
		if( !RearangeData_v_vt( *pObjModel, *pVBOModel ) )
		{
			delete pObjModel;
			delete pVBOModel;
			lastError = "MeshManager::LoadModel failed. RearangeData_v_vt failed to convert " + sname + " model!\n";
			return false;
		}
	}
	else
	if( dataInModel == V_AND_VN_DATA )
	{
		if( !RearangeData_v_vn( *pObjModel, *pVBOModel ) )
		{
			delete pObjModel;
			delete pVBOModel;
			lastError = "MeshManager::LoadModel failed. RearangeData_v_vn failed to convert " + sname + " model!\n";
			return false;
		}
	}
	else
	{
		if( !RearangeData_v( *pObjModel, *pVBOModel ) )
		{
			delete pObjModel;
			delete pVBOModel;
			lastError = "MeshManager::LoadModel failed. RearangeData_v failed to convert " + sname + " model!\n";
			return false;
		}
	}

	//udalo sie przekonwertowac dane, wkladamy zatem VBOModel do map
	namesMap.insert( pair< string, VBOModel* >( sname, pVBOModel ) );

	//sukces!
	return true;
}
//---------------------------------------------------- DrawModelElement ------------------------------------------
bool MeshManager::DrawModelElement(const char *key, uint elementNumber, DataInModel desiredData )
{
	string skey( key );
	VBOModel* pVBOModel;
	map< string, VBOModel* >::iterator it = namesMap.find( skey );

	if( it != namesMap.end() )
		pVBOModel = (*it).second;
	else
	{
		lastError = "MeshManager::DrawModelElement failed. No model with name: " + string(key) + " was found in container. ";
		return false;
	}

	DrawIthElement( pVBOModel, desiredData, elementNumber );

	return true;
}
//---------------------------------------------------- DrawModel -------------------------------------------------
bool MeshManager::DrawModel( const char* key, DataInModel desiredData )
{
	string skey( key );
	VBOModel* pVBOModel;

	if( namesMap.count( skey ) == 1 )
		pVBOModel = namesMap[ skey ];
	else
	{
		lastError = "MeshManager::DrawModel failed. No model with name: " + string(key) + " was found in container. ";
		return false;
	}

	//po wszystkich elementach w meshu
	for( unsigned int i = 0; i < pVBOModel->GetNumberOfElements(); ++i )
		DrawIthElement( pVBOModel, desiredData, i );

	return true;
}
//---------------------------------------------------- DeleteAll -------------------------------------------------
void MeshManager::DeleteAll()
{
	// 1. Wykonujemy delete na pointerach
	map< string, VBOModel* >::iterator it;
	for( it = namesMap.begin(); it != namesMap.end(); ++it )
	{
		delete (*it).second;
		(*it).second = NULL;
	}

	// 2. Czyscimy sam pojemnik
	namesMap.clear();
}
//---------------------------------------------------- DeleteResource --------------------------------------------
const bool MeshManager::DeleteResource( const char* key )
{
	typedef map< string, VBOModel* >::iterator It;

	string skey = string( key );

	// 1. Jesli key to nazwa modelu
	It it = namesMap.find( skey );
	if( it != namesMap.end() )
	{
		// 1.a czyscimy pointer
		delete ((*it).second);
		(*it).second = NULL;
	}
	else
	{
		lastError = "MeshManager::DeleteResource failed. Couldn't find model: " + string( key ) + " in mesh manager.";
		return false;
	}

	// 3. usuwamy wpis w namesMap 
	namesMap.erase( key );
	return true;
}
//---------------------------------------------------- DrawIthElement --------------------------------------------
void MeshManager::DrawIthElement( const VBOModel* pVBOModel, const DataInModel& desiredData, const uint& ith  )
{
	DataInModel dataInModel = pVBOModel->GetDataType();

	if(  desiredData == V_AND_VT_AND_VN_DATA )
	{
		if( dataInModel == V_AND_VT_AND_VN_DATA )
			DrawVBO( pVBOModel->GetVBOid( ith ), pVBOModel->GetNumberOfVertices( ith ), V_AND_VT_AND_VN_DATA, pVBOModel->GetPolygonType() );
		else
		if( dataInModel == V_AND_VT_DATA )
			DrawVBO( pVBOModel->GetVBOid( ith ), pVBOModel->GetNumberOfVertices( ith ), V_AND_VT_DATA, pVBOModel->GetPolygonType() );
		else
		if( dataInModel == V_AND_VN_DATA )
			DrawVBO( pVBOModel->GetVBOid( ith ), pVBOModel->GetNumberOfVertices( ith ), V_AND_VN_DATA, pVBOModel->GetPolygonType() );
		else
			DrawVBO( pVBOModel->GetVBOid( ith ), pVBOModel->GetNumberOfVertices( ith ), V_DATA, pVBOModel->GetPolygonType() );
	}
	else
	if( desiredData == V_AND_VT_DATA )
	{
		if( dataInModel == V_AND_VT_DATA || dataInModel == V_AND_VT_AND_VN_DATA )
			DrawVBO( pVBOModel->GetVBOid( ith ), pVBOModel->GetNumberOfVertices( ith ), V_AND_VT_DATA, pVBOModel->GetPolygonType() );
		else
			DrawVBO( pVBOModel->GetVBOid( ith ), pVBOModel->GetNumberOfVertices( ith ), V_DATA, pVBOModel->GetPolygonType() );
	}
	else
	if( desiredData == V_AND_VN_DATA )
	{
		if( dataInModel == V_AND_VN_DATA || dataInModel == V_AND_VT_AND_VN_DATA )
			DrawVBO( pVBOModel->GetVBOid( ith ), pVBOModel->GetNumberOfVertices( ith ), V_AND_VN_DATA, pVBOModel->GetPolygonType() );
		else
			DrawVBO( pVBOModel->GetVBOid( ith ), pVBOModel->GetNumberOfVertices( ith ), V_DATA, pVBOModel->GetPolygonType() );
	}
	else
		DrawVBO( pVBOModel->GetVBOid( ith ), pVBOModel->GetNumberOfVertices( ith ), V_DATA, pVBOModel->GetPolygonType() );

}
//---------------------------------------------------- DrawVBO ---------------------------------------------------
void MeshManager::DrawVBO( const uint& VBOid, const uint& numberOfVertices, const DataInModel& whichData, const int& polygonType )
{
	if( whichData == V_AND_VT_AND_VN_DATA )
	{
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOid );

		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_NORMAL_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);

		glVertexPointer( 3, GL_FLOAT, 0, 0 );
		glTexCoordPointer( 2, GL_FLOAT, 0, (void*)(numberOfVertices * 3 * sizeof(float)) );
		glNormalPointer( GL_FLOAT, 0, (void*)(numberOfVertices * (3 * sizeof(float) + 2 * sizeof(float))) );

		glDrawArrays( polygonType, 0, numberOfVertices );

		glDisableClientState(GL_NORMAL_ARRAY);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glDisableClientState(GL_VERTEX_ARRAY);

		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
	}
	else
	if( whichData == V_AND_VT_DATA )
	{
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOid );

		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);

		glVertexPointer( 3, GL_FLOAT, 0, 0 );
		glTexCoordPointer( 2, GL_FLOAT, 0, (void*)(numberOfVertices * 3 * sizeof(float)) );

		glDrawArrays( polygonType, 0, numberOfVertices);

		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glDisableClientState(GL_VERTEX_ARRAY);

		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
	}
	else
	if( whichData == V_AND_VN_DATA )
	{
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOid );

		glEnableClientState(GL_VERTEX_ARRAY);
		glEnableClientState(GL_NORMAL_ARRAY);

		glVertexPointer( 3, GL_FLOAT, 0, 0 );
		glNormalPointer( GL_FLOAT, 0, (void*)(numberOfVertices * (3 * sizeof(float) + 2 * sizeof(float))) );

		glDrawArrays( polygonType, 0, numberOfVertices );

		glDisableClientState(GL_NORMAL_ARRAY);
		glDisableClientState(GL_VERTEX_ARRAY);

		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
	}
	else
	if( whichData == V_DATA )
	{
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOid );

		glEnableClientState(GL_VERTEX_ARRAY);

		glVertexPointer( 3, GL_FLOAT, 0, 0 );

		glDrawArrays( polygonType, 0, numberOfVertices );

		glDisableClientState(GL_VERTEX_ARRAY);

		glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
	}
}
//---------------------------------------------------- InitVBO ---------------------------------------------------
void MeshManager::InitVBO()
{
	const char* ext = (const char *)glGetString(GL_EXTENSIONS);

	// check VBO is supported by your video card
	if( string( ext ).find( "GL_ARB_vertex_buffer_object" ) )
	{
		// get pointers to GL functions
		glGenBuffersARB = (PFNGLGENBUFFERSARBPROC)wglGetProcAddress("glGenBuffersARB");
		glBindBufferARB = (PFNGLBINDBUFFERARBPROC)wglGetProcAddress("glBindBufferARB");
		glBufferDataARB = (PFNGLBUFFERDATAARBPROC)wglGetProcAddress("glBufferDataARB");
		glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC)wglGetProcAddress("glBufferSubDataARB");
		glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC)wglGetProcAddress("glDeleteBuffersARB");
		glGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC)wglGetProcAddress("glGetBufferParameterivARB");
		glMapBufferARB = (PFNGLMAPBUFFERARBPROC)wglGetProcAddress("glMapBufferARB");
		glUnmapBufferARB = (PFNGLUNMAPBUFFERARBPROC)wglGetProcAddress("glUnmapBufferARB");

		// check once again VBO extension
		if(glGenBuffersARB && glBindBufferARB && glBufferDataARB && glBufferSubDataARB &&
		   glMapBufferARB && glUnmapBufferARB && glDeleteBuffersARB && glGetBufferParameterivARB)
		{		
			//todo: log Info Here
		}
		else
		{
			//logout << "VBO pointers failed" << endl;
			exit(0);
			//todo: log Info Here
		}
	}
	else
	{
		//logout << "VBO nie obslugowane przez karte graficzna " << endl;
		exit(0);
		//todo: log Info Here
	}
}
//------------------------------------------------- RearangeData_v_vt_vn -----------------------------------------
bool MeshManager::RearangeData_v_vt_vn( ObjModel& M, VBOModel& vboModel )
{
	struct V
	{ 
		float x, y, z; 

		void operator=( ObjVertex& ov )
		{ x = ov.x;  y = ov.y;  z = ov.z; }
	};

	struct VN
	{
		float x, y, z; 

		void operator=( ObjNormal& on )
		{ x = on.x;  y = on.y;  z = on.z; }
	};

	struct VT
	{
		float x, y;
	
		void operator=( ObjTexCoord& otc )
		{ x = otc.x;  y = otc.y; }
	};

	for( unsigned int ii = 0; ii < M.modelElements.size(); ++ii )
	{
		vboModel.numberOfVertices[ ii ] = M.modelElements[ ii ].faces.size() * M.GetNumberOfVerticesPerFace();
		vboModel.names[ ii ] = M.modelElements[ ii ].name;

		V* tmpV = new V[ vboModel.numberOfVertices[ ii ] ];
		VN* tmpVN = new VN[ vboModel.numberOfVertices[ ii ] ];
		VT* tmpVT = new VT[ vboModel.numberOfVertices[ ii ] ];

		//idziemy po wszsytkich fejsach i kopiujemy dane
		for( unsigned int jj = 0; jj < M.modelElements[ ii ].faces.size(); ++jj )
			for( int kk = 0; kk < M.GetNumberOfVerticesPerFace(); ++kk )
			{
				//wyliczamy indeksy
				int vId = M.modelElements[ ii ].faces[ jj ].vertex[ kk ];
				int vnId = M.modelElements[ ii ].faces[ jj ].normal[ kk ];
				int vtId = M.modelElements[ ii ].faces[ jj ].texCoord[ kk ];
				int bId = kk + jj * M.GetNumberOfVerticesPerFace();

				//kopiujemy dane
				tmpV[ bId ] = M.vertices[ vId - 1 ];
				tmpVN[ bId ] = M.normals[ vnId - 1 ];
				tmpVT[ bId ] = M.texCoords[ vtId - 1 ];
			}

		//mamy dane. tworzymy VBO
		glGenBuffersARB( 1, &vboModel.VBOids[ ii ] );
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboModel.VBOids[ ii ] );
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
						 2 * vboModel.numberOfVertices[ ii ] * sizeof( V ) + vboModel.numberOfVertices[ ii ] * sizeof( VT ),
						 NULL,
						 GL_STATIC_DRAW_ARB
			  		   );
		
		//wkoncu przepisujemy
		glBufferSubDataARB( GL_ARRAY_BUFFER_ARB, 0, vboModel.numberOfVertices[ ii ] * sizeof( V ), (void*)tmpV ); //czy tos ie napewno dobrze przekopiuje?
		glBufferSubDataARB( GL_ARRAY_BUFFER_ARB, vboModel.numberOfVertices[ ii ] * sizeof( V ), vboModel.numberOfVertices[ ii ] * sizeof(VT), (void*)tmpVT ); 
		glBufferSubDataARB( GL_ARRAY_BUFFER_ARB, vboModel.numberOfVertices[ ii ] * (sizeof( V ) + sizeof( VT )), vboModel.numberOfVertices[ ii ] * sizeof( VN ), (void*)tmpVN ); 

		//sprawdzamy zawartosc VBO
		int dataSize = vboModel.numberOfVertices[ ii ] * ( sizeof(V) + sizeof(VT) + sizeof(VN) );
		int bufferSize = 0;

		glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &bufferSize);
		if(dataSize != bufferSize)
		{
			return false;
		}

		//pobieramy wskaznik do VBO, w ten sposob mozemy wypisac zawartosc VBO - do debugu
		//pVBO[ ii ] = (float*)glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB );

		//zwalniamy tymczasowe bufory
		delete[] tmpV;
		delete[] tmpVN;
		delete[] tmpVT;
	}

	return true;
}

//-------------------------------------------------- RearangeData_v ----------------------------------------------
bool MeshManager::RearangeData_v( ObjModel& M, VBOModel& vboModel )
{
	struct V
	{ 
		float x, y, z; 

		void operator=( ObjVertex& ov )
		{ x = ov.x;  y = ov.y;  z = ov.z; }
	};

	for( unsigned int ii = 0; ii < M.modelElements.size(); ++ii )
	{
		vboModel.numberOfVertices[ ii ] = M.modelElements[ ii ].faces.size() * M.GetNumberOfVerticesPerFace();
		vboModel.names[ ii ] = M.modelElements[ ii ].name;

		V* tmpV = new V[ vboModel.numberOfVertices[ ii ] ];
		//fV[ii] = new float[ numberOfVertices[ ii ] * 3 ];

		//idziemy po wszsytkich fejsach i kopiujemy dane
		for( unsigned int jj = 0; jj < M.modelElements[ ii ].faces.size(); ++jj )
			for( int kk = 0; kk < M.GetNumberOfVerticesPerFace(); ++kk )
			{
				//wyliczamy indeksy
				int vId = M.modelElements[ ii ].faces[ jj ].vertex[ kk ];
				int bId = kk + jj * M.GetNumberOfVerticesPerFace();

				//kopiujemy dane
				tmpV[ bId ] = M.vertices[ vId - 1 ];

				//kopiujemy dane do zwyklej tabicy 
				//fV[ii][ jj * M.GetNumberOfVerticesPerFace() * 3 + kk * 3  + 0 ] = M.vertices[ vId - 1 ].x;
				//fV[ii][ jj * M.GetNumberOfVerticesPerFace() * 3 + kk * 3  + 1 ] = M.vertices[ vId - 1 ].y;
				//fV[ii][ jj * M.GetNumberOfVerticesPerFace() * 3 + kk * 3  + 2 ] = M.vertices[ vId - 1 ].z;
			}

		//mamy dane. tworzymy VBO
		glGenBuffersARB( 1, &vboModel.VBOids[ ii ] );
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboModel.VBOids[ ii ] );
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
						 vboModel.numberOfVertices[ ii ] * sizeof( V ),
						 (void *)tmpV,
						 GL_STATIC_DRAW_ARB
			  		   );

		//mamy dane. tworzymy VBO. zwykla tablica
		//glGenBuffersARB( 1, &VBOids[ ii ] );  //VBOidsG[ ii ] = VBOids[ ii ];
		//glBindBufferARB( GL_ARRAY_BUFFER_ARB, VBOids[ ii ] );
		//glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
		//				 numberOfVertices[ ii ] * 3 * sizeof(float),
		//				 (void *)(fV[ii]),
		//				 GL_STATIC_DRAW_ARB
		//	  		   );

		//sprawdzamy zawartosc VBO
		int dataSize = vboModel.numberOfVertices[ ii ] * 3 * sizeof(float);
		int bufferSize = 0;

		glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &bufferSize);
		if(dataSize != bufferSize)
		{
			return false;
		}

		//pobieramy wskaznik do VBO, w ten sposob mozemy wypisac zawartosc VBO - do debugu
		//pVBO[ ii ] = (float*)glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB );

		//zwalniamy tymczasowe bufory
		delete[] tmpV;
	}

	//sukces
	return true;
}
//-------------------------------------------------- RearangeData_v_vt -------------------------------------------
bool MeshManager::RearangeData_v_vt( ObjModel& M, VBOModel& vboModel )
{
	struct V
	{ 
		float x, y, z; 

		void operator=( ObjVertex& ov )
		{ x = ov.x;  y = ov.y;  z = ov.z; }
	};

	struct VT
	{
		float x, y;
	
		void operator=( ObjTexCoord& otc )
		{ x = otc.x;  y = otc.y; }
	};

	for( unsigned int ii = 0; ii < M.modelElements.size(); ++ii )
	{
		vboModel.numberOfVertices[ ii ] = M.modelElements[ ii ].faces.size() * M.GetNumberOfVerticesPerFace();
		vboModel.names[ ii ] = M.modelElements[ ii ].name;

		V* tmpV = new V[ vboModel.numberOfVertices[ ii ] ];
		VT* tmpVT = new VT[ vboModel.numberOfVertices[ ii ] ];

		//idziemy po wszsytkich fejsach i kopiujemy dane
		for( unsigned int jj = 0; jj < M.modelElements[ ii ].faces.size(); ++jj )
			for( int kk = 0; kk < M.GetNumberOfVerticesPerFace(); ++kk )
			{
				//wyliczamy indeksy
				int vId = M.modelElements[ ii ].faces[ jj ].vertex[ kk ];
				int vtId = M.modelElements[ ii ].faces[ jj ].texCoord[ kk ];
				int bId = kk + jj * M.GetNumberOfVerticesPerFace();

				//kopiujemy dane
				tmpV[ bId ] = M.vertices[ vId - 1 ];
				tmpVT[ bId ] = M.texCoords[ vtId - 1 ];
			}

		//mamy dane. tworzymy VBO
		glGenBuffersARB( 1, &vboModel.VBOids[ ii ] );
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboModel.VBOids[ ii ] );
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
						 vboModel.numberOfVertices[ ii ] * sizeof( V ) + vboModel.numberOfVertices[ ii ] * sizeof( VT ),
						 NULL,
						 GL_STATIC_DRAW_ARB
			  		   );
		
		//wkoncu przepisujemy
		glBufferSubDataARB( GL_ARRAY_BUFFER_ARB, 0, vboModel.numberOfVertices[ ii ] * sizeof( V ), (void*)tmpV ); //czy tos ie napewno dobrze przekopiuje?
		glBufferSubDataARB( GL_ARRAY_BUFFER_ARB, vboModel.numberOfVertices[ ii ] * sizeof( V ), vboModel.numberOfVertices[ ii ] * sizeof(VT), (void*)tmpVT ); 

		//sprawdzamy zawartosc VBO
		int dataSize = vboModel.numberOfVertices[ ii ] * ( sizeof(V) + sizeof(VT) );
		int bufferSize = 0;

		glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &bufferSize);
		if(dataSize != bufferSize)
		{
			return false;
		}

		//pobieramy wskaznik do VBO, w ten sposob mozemy wypisac zawartosc VBO - do debugu
		//pVBO[ ii ] = (float*)glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB );

		//zwalniamy tymczasowe bufory
		delete[] tmpV;
		delete[] tmpVT;
	}

	return true;
}
//-------------------------------------------------- RearangeData_v_vn -------------------------------------------
bool MeshManager::RearangeData_v_vn( ObjModel& M, VBOModel& vboModel )
{
	/*
		Zagadka - dlaczego tutaj musimy alokowac zbedne miejsce na wspolrzedne tekstur (ktorych nie ma)?
		Otoz musimy utworzyc bufor [V(na dole)  VT(po srodku)  VN(na szczcycie)] aby umiec poprawnie 
		zaindeksowac wskazniki na odpowiedznie miejca stosu w funkcji DrawVBO.  
	*/

	struct V
	{ 
		float x, y, z; 

		void operator=( ObjVertex& ov )
		{ x = ov.x;  y = ov.y;  z = ov.z; }
	};

	struct VN
	{
		float x, y, z; 

		void operator=( ObjNormal& on )
		{ x = on.x;  y = on.y;  z = on.z; }
	};

	struct VT
	{
		float x, y;
	};

	for( unsigned int ii = 0; ii < M.modelElements.size(); ++ii )
	{
		vboModel.numberOfVertices[ ii ] = M.modelElements[ ii ].faces.size() * M.GetNumberOfVerticesPerFace();
		vboModel.names[ ii ] = M.modelElements[ ii ].name;

		V* tmpV = new V[ vboModel.numberOfVertices[ ii ] ];
		VN* tmpVN = new VN[ vboModel.numberOfVertices[ ii ] ];

		//idziemy po wszsytkich fejsach i kopiujemy dane
		for( unsigned int jj = 0; jj < M.modelElements[ ii ].faces.size(); ++jj )
			for( int kk = 0; kk < M.GetNumberOfVerticesPerFace(); ++kk )
			{
				//wyliczamy indeksy
				int vId = M.modelElements[ ii ].faces[ jj ].vertex[ kk ];
				int vnId = M.modelElements[ ii ].faces[ jj ].normal[ kk ];
				int bId = kk + jj * M.GetNumberOfVerticesPerFace();

				//kopiujemy dane
				tmpV[ bId ] = M.vertices[ vId - 1 ];
				tmpVN[ bId ] = M.normals[ vnId - 1 ];
			}

		//mamy dane. tworzymy VBO
		glGenBuffersARB( 1, &vboModel.VBOids[ ii ] );
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, vboModel.VBOids[ ii ] );
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, 
						 2 * vboModel.numberOfVertices[ ii ] * sizeof( V ) + vboModel.numberOfVertices[ ii ] * sizeof( VT ),
						 NULL,
						 GL_STATIC_DRAW_ARB
			  		   );
		
		//wkoncu przepisujemy
		glBufferSubDataARB( GL_ARRAY_BUFFER_ARB, 0, vboModel.numberOfVertices[ ii ] * sizeof( V ), (void*)tmpV ); 
		glBufferSubDataARB( GL_ARRAY_BUFFER_ARB, vboModel.numberOfVertices[ ii ] * (sizeof( V ) + sizeof( VT )), vboModel.numberOfVertices[ ii ] * sizeof( VN ), (void*)tmpVN ); 

		//sprawdzamy zawartosc VBO
		int dataSize = vboModel.numberOfVertices[ ii ] * ( sizeof(V) + sizeof(VN) + sizeof(VT) );
		int bufferSize = 0;

		glGetBufferParameterivARB(GL_ARRAY_BUFFER_ARB, GL_BUFFER_SIZE_ARB, &bufferSize);
		if(dataSize != bufferSize)
		{
			return false;
		}

		//pobieramy wskaznik do VBO, w ten sposob mozemy wypisac zawartosc VBO - do debugu
		//pVBO[ ii ] = (float*)glMapBufferARB( GL_ARRAY_BUFFER_ARB, GL_READ_WRITE_ARB );

		//zwalniamy tymczasowe bufory
		delete[] tmpV;
		delete[] tmpVN;
	}

	return true;
}